import { any, must } from "./core/logic";
import { Evented, Event, EventType } from "./event/index";
import { adjustCanvas, EXTENSION_FORMATS, guid } from "./core/utils";
import { getJson } from "./core/ajax";
import { Scene } from "./scene/Scene";
import { Res } from "./assets/Res";
import { Register } from "./register/Register";
import { TransSystem } from "./system/TransSystem";
import { MeshSystem } from "./system/MeshSystem";
import { RenderSystem } from "./system/RenderSystem";
import { LightSystem } from "./system/LightSystem";
import { IdleSystem } from "./system/IdleSystem";
import { WebGPUMipmapGenerator } from "./core/TextureMipmapGenerator";
import { skyPng } from "./assets/loader/defaultSky";
import { Pass } from "./component/post/Pass";
import { Command } from "./Command";
import { ComponentType } from "./component/index";
/**
 * w3d引擎,所有的能力开发需要在Engine.init后进行，Engine.init是一个异步函数。可在then或callback中进行下一步操作。
 * @class
 * @memberof w
 */
class Engine extends Evented {
  static instance = null;
  /**
   * Engine的构造函数。
   * @constructor
   * @param {*} opts
   * @param {HTMLElement} opts.canvas 【必须】画布对象，3D物体可视化的载体。
   * @param {Boolean} opts.contextmunu 【可选,缺省值:false】是否支持右键菜单。
   * @param {String} opts.alphaMode 【可选,缺省值:opaque】合成到画布上时的透明度选项。
   * @param {Object} opts.deviceDescriptor 【可选,缺省值:{}】描述适配器请求设备的对象,可设置值如下:
   * {
   *  requiredLimits:{
   *     maxBindGroups: 4,
   *     maxBindingsPerBindGroup:1000,
   *     maxBufferSize:268435456,
   *     maxColorAttachmentBytesPerSample:32,
   *     maxColorAttachments:8,
   *     maxComputeInvocationsPerWorkgroup:256,
   *     maxComputeWorkgroupSizeX:256,
   *     maxComputeWorkgroupSizeY:256,
   *     maxComputeWorkgroupSizeZ:64,
   *     maxComputeWorkgroupStorageSize:16384,
   *     maxComputeWorkgroupsPerDimension:65535,
   *     maxDynamicStorageBuffersPerPipelineLayout:4,
   *     maxDynamicUniformBuffersPerPipelineLayout:8,
   *     maxInterStageShaderComponents:60,
   *     maxInterStageShaderVariables:16,
   *     maxSampledTexturesPerShaderStage:16,
   *     maxSamplersPerShaderStage:16,
   *     maxStorageBufferBindingSize:134217728,
   *     maxStorageBuffersPerShaderStage:8,
   *     maxStorageTexturesPerShaderStage:4,
   *     maxTextureArrayLayers:256,
   *     maxTextureDimension1D:8192,
   *     maxTextureDimension2D:8192,
   *     maxTextureDimension3D:2048,
   *     maxUniformBufferBindingSize:65536,
   *     maxUniformBuffersPerShaderStage:12,
   *     maxVertexAttributes:16,
   *     maxVertexBufferArrayStride:2048,
   *     maxVertexBuffers:8,
   *     minStorageBufferOffsetAlignment:256,
   *     minUniformBufferOffsetAlignment:256
   *  }
   * }
   * @param {String} opts.auth
   * @param {Boolean} opts.runAnimation 是否自动执行动画，缺省true
   * @param {String} opts.powerPreference 【可选，缺省值：high-performance】浏览器在运行WebGPU上下文时使用相应的GPU电源配置,可能值如下:"default":自动选择，默认值。|"high-performance": 高性能模式。|"low-power": 节能模式。
   * @param {Color} opts.clearColor 【可选,缺省值:"#000000"】画布的清屏颜色。
   * @param {Color} opts.ambientColor 【可选,缺省值:"#ffffff"】全局环境光颜色。
   * @param {Number} opts.ambientIntensity 【可选,缺省值:0.0】全局环境光强度。
   * @param {Boolean} opts.enablePick 【可选，缺省值:false】是否支持拾取。仅当值为true才可以拾取物体进行交互。当为true可以通过{opts.pickMode}来设置自己的拾取模式。
   * @param {String} opts.pickMode 【可选,缺省值:"pixel"】三维场景中物体拾取的模式：可选值如下："pixel"(基于像素拾取)|"ray"(基于射线拾取)。
   * @param {Boolean} opts.shadowEnable 【可选，缺省值：false】值设置为false，场景不会产生阴影。值设置为true，同时场景中添加了cast=true(投射阴影)的光源时会产生阴影效果。
   * @param {Array<String>} opts.background 【必选】场景背景，六张图片构成的全景图。
   * @param {Array<String>} opts.environment 【必选】场景ibl环境光，六张图片构成的全景图。
   * @param {Number} opts.intensity 【可选,缺省值:1.0】场景ibl环境光强度。
   * @param {Boolean} opts.enableGbuffer 【可选,缺省值:false】是否开启G-Buffer。部分后处理依赖该功能的开启。{@link w.effects.GTAO}{@link w.effects.GlobalFog}
   * @param {Boolean} opts.logarithmicDepth 【可选,缺省值:true】是否使用对数深度,对数深度能给远处更多的深度精度。
   * @param {String} opts.sceneId  
  */
  constructor(opts = {}) {
    if (Engine.instance) {
      return Engine.instance;
    }
    super();
    this.runAnimation = any(opts.runAnimation, true);
    //初始化注册类
    this._register = new Register();
    this.register.systemManager.add(new MeshSystem({ id: "meshsystem" }));
    this.register.systemManager.add(new TransSystem({ id: "transsystem" }));
    // this.register.systemManager.add(new PackSystem());
    this.register.systemManager.add(new RenderSystem({ id: "rendersystem" }));
    this.register.systemManager.add(new LightSystem({ id: "lightsystem" }));
    this.register.systemManager.add(new IdleSystem({ id: "idlesystem" })); //注册并激活idle执行系统。
    // this.register.systemManager.add(new PickSystem());
    //初始化资源类
    this._res = new Res();
    this._canvas = must(opts.canvas); //1
    this.obver = new ResizeObserver((e) => {
      const size = adjustCanvas(this.canvas);
      let ev = new Event(EventType.Resize);
      ev.size = size;
      this.publish(ev); //抛出舞台改变事件
    });
    this.obver.observe(this.canvas);
    this.center = any(opts.center, [0, 0]);
    this._contextmenu = any(opts.contextmenu, false);
    this.pickMode = any(opts.pickMode, "pixel");
    this._alphaMode = any(opts.alphaMode, "opaque"); //1
    this._deviceDescriptor = any(opts.deviceDescriptor, {}); //1
    this._powerPreference = any(opts.powerPreference, "high-performance"); //1
    this._clearColor = any(opts.clearColor, "#000000");
    this._ambientColor = any(opts.ambientColor, "#ffffff");
    this._ambientIntensity = any(opts.ambientIntensity, 1.0);
    this.opencursor = opts.opencursor;
    this.closecursor = opts.closecursor;
    this.sceneId=any(opts.sceneId,guid())
    this._$limit = { ec: 300 };
    if (this.contextmenu == false) {
      this.canvas.addEventListener("contextmenu", (event) => {
        event.preventDefault(); //阻止右键默认行为
      });
    }
    this.background = opts.background;
    this.environment = opts.environment;
    this.gamma = any(opts.gamma, 1.8);
    this._intensity = any(opts.intensity, 1.0);
    this.enableGbuffer = any(opts.enableGbuffer, false);
    this.shadowEnable = any(opts.shadowEnable, false);
    this.logarithmicDepth = any(opts.logarithmicDepth, false);
    this._enablePick = any(opts.enablePick, false);
    this.multisample = any(opts.multisample, true);
    this._command = new Command(this);
    Engine.instance = this;
  }
  set intensity(v) {
    this._intensity = v;
    //更新环节光
    let e = this.register.manager(ComponentType.EnviromentLight).get("x");
    if (e) {
      let b = e.getParam('environmenIntensity');
      if (b) {
        b.buffer(new Float32Array([v]));
        this.register.systemManager.get("rendersystem").frame();
      }
    }
    return this;
  }
  get intensity() {
    return this._intensity;
  }
  /**
   * 获取指令执行上下文
   */
  get command() {
    return this._command;
  }
  /**
   * @property {Register} 注册表，里面包含引擎运行所需要的全局项。
   */
  get register() {
    return this._register;
  }
  /**
   * @property {Res} 资源管理类，里面包含引擎运行需要的资源加载类及资源管理。
   */
  get res() {
    return this._res;
  }

  /**
   * @property  {Boolean} enablePick 【只读,缺省值:false】引擎是否开启拾取功能。
   */
  get enablePick() {
    return this._enablePick;
  }
  get pickMode() {
    return this._pickMode;
  }
  set pickMode(v) {
    this._pickMode = v;
    return this;
  }
  set clearColor(v) {
    this._clearColor = v;
    return this;
  }
  get clearColor() {
    return this._clearColor;
  }
  set canvas(v) {
    this._canvas = v;
    return this;
  }
  get canvas() {
    return this._canvas;
  }
  set contextmenu(v) {
    this._contextmenu = v;
    return this;
  }
  get contextmenu() {
    return this._contextmenu;
  }
  set alphaMode(v) {
    this._alphaMode = v;
    return this;
  }
  get alphaMode() {
    return this._alphaMode;
  }
  set deviceDescriptor(v) {
    this._deviceDescriptor = v;
    return this;
  }
  get deviceDescriptor() {
    return this._deviceDescriptor;
  }
  set powerPreference(v) {
    this._powerPreference = v;
    return this;
  }
  get powerPreference() {
    return this._powerPreference;
  }
  get size() {
    return [this.canvas.width, this.canvas.height];
  }
  /**
   * 渲染引擎初始化。
   * @method
   * @param {function} callback  初始化完成执行的回调函数
   * @example
   * let engine=new w.Engine(opts);
   * //三种初始化方式
   * engine.init().then(this.init);//方式1：回调方式
   * engine.init(this.init);//方式2：Promise
   * await engine.init();//方式3：同步方式
   * @return {w.Scene} 返回创建的默认场景。
   */
  async init(callback) {
    if (!navigator.gpu) {
      alert("不支持渲染，请确保使用支持的浏览器版本并确保使用https访问");
      throw new Error(
        "不支持渲染，请确保使用支持的浏览器版本并确保使用https访问"
      );
    }
    Object.freeze(this._$limit);

    this.context = this.canvas.getContext("webgpu");
    this.adapter = await navigator.gpu.requestAdapter({
      powerPreference: this.powerPreference, //high-performance low-power
    });
    if (!this.adapter) {
      alert("不支持渲染,请确保使用支持的显卡并确保使用https访问！");
      throw new Error("不支持渲染,请确保使用支持的显卡并确保使用https访问！");
    }
    this.deviceDescriptor = {
      requiredFeatures: this.adapter.features,
      requiredLimits: {
        maxColorAttachmentBytesPerSample: 40,
      },
    };
    this.device = await this.adapter.requestDevice(this.deviceDescriptor);
    this.supportedFormatList = [
      "rgba8unorm",
      "rgba8unorm-srgb",
      "bgra8unorm",
      "bgra8unorm-srgb",
      "rg11b10ufloat",
    ];
    const featureList = this.device.features;
    if (featureList) {
      for (const feature in EXTENSION_FORMATS) {
        if (featureList.has(feature)) {
          const formats = EXTENSION_FORMATS[feature];
          this.supportedFormatList.push(...formats);
        }
      }
    }
    this.queue = this.device.queue;
    this.format = "rgba8unorm";
    this.context.configure({
      device: this.device,
      format: this.format,
      alphaMode: this.alphaMode,
    });
    this.canvasTexture = this.context.getCurrentTexture()
    // this.canvasTexture.view = this.canvasTexture.createView()
    //创建缺省场景。
    this.scene = await this.createScene();
    this.mipmapGenerator = new WebGPUMipmapGenerator(this.device);
    await this.loadEnviroment();
    this.register.systemManager.active();//激活系统.
    this.scene.background = this.background
    this.scene.environment = this.environment
    if (callback) {
      callback(this);
    }
    return this
  }
  async loadEnviroment() {
    if (!this.background || !this.environment) {
      if (!this.background && this.environment) {
        const CubeMap = await this.res.textureLoader.loadSkyBox(
          this.environment,
          this.device,
          this.format,
          true,
          this.context
        );
        this.background = CubeMap.cubeMap;
        this.environment = CubeMap.ibl;
      }
      if (this.background && !this.environment) {
        // skybox使用传进来的
        const textureCubeMap = await this.res.textureLoader.loadSkyBox(
          this.background,
          this.device,
          this.format,
          true,
          this.context
        );
        this.background = textureCubeMap.cubeMap;
        this.environment = textureCubeMap.ibl;
      }
      if (!this.background && !this.environment) {
        // skybox使用传进来的
        const textureCubeMap = await this.res.textureLoader.loadSkyBox(
          skyPng,
          this.device,
          this.format,
          true,
          this.context
        );
        this.background = textureCubeMap.cubeMap;
        this.environment = textureCubeMap.ibl;
      }
    } else {
      if (this.background === this.environment) {
        //如果两个路径相等
        // 判断是hdr还是六张纹理
        const textureCubeMap = await this.res.textureLoader.loadSkyBox(
          this.background,
          this.device,
          this.format,
          true,
          this.context
        );
        this.background = textureCubeMap.cubeMap;
        this.environment = textureCubeMap.ibl;
      } else {
        //如果路径不相等
        const textureCubeMap = await this.res.textureLoader.loadSkyBox(
          this.background,
          this.device,
          this.format,
          false,
          this.context,
          "background"
        );
        this.background = textureCubeMap;
        const CubeMap = await this.res.textureLoader.loadSkyBox(
          this.environment,
          this.device,
          this.format,
          true,
          this.context,
          "environment"
        );
        this.environment = CubeMap.ibl;
      }
    }
  }
  async createScene() {
    if (this.scene) {
      throw new Error("场景已经存在，不能重复创建，需要创建请先销毁");
    }
    let scene = new Scene({ engine: this,id:this.sceneId });
    await scene.init({
      ambientColor: this._ambientColor,
      ambientIntensity: this._ambientIntensity,
    });
    return scene;
  }
  destroy(canvasDistroy = false) {
    this.obver.unobserve(this.canvas);
    this.obver = null;
    this.scene.destroy(canvasDistroy);
    this.register.destroy();
    this._register = null;
    this.res.destroy();
    this._res = null;
    this.format = null;
    this.adapter = null;
    this.device = null;
    this.queue = null;
    this.scene = null;

    // this.canvas=null;
    this.context = null;
    this._contextmenu = null;
    this.pickMode = null;
    this._alphaMode = null;
    this._deviceDescriptor = null;
    this._powerPreference = null;
    this._clearColor = null;
    this._ambientColor = null;
    this._ambientIntensity = null;
    this.opencursor = null;
    this.closecursor = null;
    this.background = null;
    this.environment = null;
    this.gamma = null;
    // this.intensity = null;
    this.enableGbuffer = null;
    this.shadowEnable = null;
    this.logarithmicDepth = null;
    this._enablePick = null;
    this.supportedFormatList = null;
    this.mipmapGenerator = null;
    Pass.instance().destroy();
    Engine.instance = null;
  }
}
export { Engine };
